package com.fm.webreader.html;

import com.fm.webreader.html.table.HtmlTable;
import com.fm.webreader.html.table.HtmlTableReader;
import org.apache.commons.lang3.StringEscapeUtils;
import org.jsoup.helper.StringUtil;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

public class JsoupReader {
	private static Set<String> posLineTags;
	private static Set<String> newLineTags;
	private static Set<String> uselessTags;

	static {
		posLineTags = new HashSet<>();
		posLineTags.add("tr");
		posLineTags.add("li");
		posLineTags.add("p");
		posLineTags.add("div");

		newLineTags = new HashSet<>();
		newLineTags.add("br");

		uselessTags = new HashSet<>();
		uselessTags.add("title");
		uselessTags.add("style");
		uselessTags.add("script");
	}

	public static List<Object> readDocument(Document doc) {
		return readNodes(doc.childNodes());
	}

	public static List<Object> readDocument(Document doc, JsoupFilter filter) {
		return readNodes(doc.childNodes(), filter);
	}

	public static List<Object> readNode(Node node) {
		return readNodes(node.childNodes());
	}

	public static List<Object> readNode(Node node, JsoupFilter filter) {
		return readNodes(node.childNodes(), filter);
	}

	public static List<Object> readNodes(List<? extends Node> nodes) {
		return readNodes(nodes, new JsoupFilterImpl());
	}

	public static List<Object> readNodes(List<? extends Node> nodes, JsoupFilter filter) {
		List<Object> list = new LinkedList<>();
		StringBuilder cache = new StringBuilder();
		readNodes(nodes, filter, list, cache);
		if (cache.length() > 0) {
			list.add(cache.toString());
		}
		return list;
	}

	private static boolean hasNextNode(Node node) {
		if (node == null) {
			return false;
		} else if (node instanceof TextNode) {
			if (!node.toString().trim().isEmpty()) {
				String str = StringEscapeUtils.unescapeHtml4(node.toString());
				return !str.startsWith("<!")
					&& !str.startsWith("<!--")
					&& !str.endsWith("-->")
					&& hasNextNode(node.nextSibling());
			} else {
				return hasNextNode(node.nextSibling());
			}
		} else {
			return true;
		}
	}

	private static void readNodes(List<? extends Node> nodes, JsoupFilter filter, List<Object> list, StringBuilder cache) {
		for (Node node : nodes) {
			if (node instanceof Element) {
				String tagName = ((Element) node).tagName();
				if (!uselessTags.contains(tagName)) {
					if (filter.isTable((Element) node)) {
						list.add(cache.toString());
						cache.setLength(0);
						HtmlTable<String> table = HtmlTableReader.readTable((Element) node);
						list.add(table);
					} else {
						List<Node> children = node.childNodes();
						if (children.isEmpty()) {
							String text = ((Element) node).text();
							cache.append(text);
							if (posLineTags.contains(tagName) && hasNextNode(node.nextSibling())) {
								if (!text.isEmpty()) {
									cache.append("\n");
								}
							}
						} else {
							readNodes(children, filter, list, cache);
							if (posLineTags.contains(tagName) && hasNextNode(node.nextSibling())) {
								cache.append("\n");
							}
						}
						if (newLineTags.contains(tagName)) {
							cache.append("\n");
						}
					}
				}
			} else {
				if (!node.toString().trim().isEmpty()) {
					String str = StringEscapeUtils.unescapeHtml4(node.toString());
					if (!str.startsWith("<!") && !str.startsWith("<!--") && !str.endsWith("-->")) {
						StringUtil.appendNormalisedWhitespace(cache, str, true);
					}
				}
			}
		}
	}
}
